/************
 * echo_d   *
 ************/

#include <iostream>
#include <winsock2.h>
#include <string>
#include <time.h>
using namespace std;
#pragma comment (lib, "ws2_32.lib")

#define MAXBUF  4096
#define MAXIPBUF  20

SOCKET passivesock (char*, char*);
char* timestamp();
int wait(SOCKET, int, int);


const int  MAX_HOSTNAME  = 1024;
const int  MAX_PORT      = 128;
const int  MAX_RECV_BUF  = 2048;
const int  MAX_SEND_BUF  = 128;
const int  WAIT_TIME = 15;
const int TIMEBUF_SIZE = 26;

/**********************************************
 * main - TCP server for ECHO service         *
 **********************************************/

void chomp( char* s) 
{	
	int i = 0;
	while ( s[i] != '\0' )
	{
		i++;
	}				
	if ( s[i-1] == '\n' )
	{
		s[i-1] = '\0';
	}	
}

/*****************************************************************************
*
*****************************************************************************/
char* timestamp()
{	
	static char time_s[TIMEBUF_SIZE];
	time_t ltime;							

	time( &ltime );							
	ctime_s( time_s, TIMEBUF_SIZE, &ltime );	
	chomp( time_s );

	return time_s;
}

/*****************************************************************************
*
*****************************************************************************/
char* GetHostIP()
{
    char szLclHost [MAX_HOSTNAME];
    LPHOSTENT lpstHostent;
    SOCKADDR_IN stLclAddr;
    SOCKADDR_IN stRmtAddr;
	struct in_addr stIPAddr;
    int nAddrSize = sizeof(SOCKADDR);
    SOCKET hSock;
    int nRet;
    
    /* Init local address (to zero) */
    stLclAddr.sin_addr.s_addr = INADDR_ANY;
    
    /* Get the local hostname */
    nRet = gethostname(szLclHost, MAX_HOSTNAME); 
    if (nRet != SOCKET_ERROR) 
	{
      /* Resolve hostname for local address */
      lpstHostent = gethostbyname((LPSTR)szLclHost);
      if (lpstHostent)
        stLclAddr.sin_addr.s_addr = *((u_long FAR*) (lpstHostent->h_addr));
    } 
    
    /* If still not resolved, then try second strategy */
    if (stLclAddr.sin_addr.s_addr == INADDR_ANY) 
	{
      /* Get a UDP socket */
      hSock = socket(AF_INET, SOCK_DGRAM, 0);
      if (hSock != INVALID_SOCKET) 
	  {
        /* Connect to arbitrary port and address (NOT loopback) */
        stRmtAddr.sin_family = AF_INET;
        stRmtAddr.sin_port   = htons(IPPORT_ECHO);
        stRmtAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
        nRet = connect( hSock, (LPSOCKADDR)&stRmtAddr, sizeof( SOCKADDR ));
        if ( nRet != SOCKET_ERROR ) 
		{
          getsockname( hSock, (LPSOCKADDR)&stLclAddr, (int FAR*)&nAddrSize );
        }
        closesocket( hSock );   
      }
    }
	stIPAddr.s_addr = stLclAddr.sin_addr.s_addr;
    return (inet_ntoa(stIPAddr));
} 

/*****************************************************************************
*
*****************************************************************************/
SOCKET passivesock ( char *service, char *protocol ) 
{
	servent *pse;		
	sockaddr_in server;	
	SOCKET s;			

	if ( stricmp( protocol,"tcp" ) == 0 )
		s = socket( AF_INET,SOCK_STREAM,0 );
	else if ( stricmp( protocol,"udp ") == 0 )
		s = socket( AF_INET,SOCK_DGRAM,0 );
	else
		s = INVALID_SOCKET;
	if ( s < 0 || s == INVALID_SOCKET ) 
	{
		cout << "Cannot create socket" << endl;
		return INVALID_SOCKET;
	}

	memset( &server, 0, sizeof( server ));		

	server.sin_family = AF_INET;		
	server.sin_addr.s_addr = INADDR_ANY;	

	if ( pse = getservbyname( service,protocol ))
		server.sin_port = (u_short) pse->s_port;
	else if ( (server.sin_port = htons( (u_short) atoi( service ))) == 0 )
	{
		cout << "Invalid service request" << endl;
		return INVALID_SOCKET;
	}

	if ( bind(s, (LPSOCKADDR) &server, sizeof(SOCKADDR)) < 0 )
	{
		cout << "Cannot bind socket to port: " << service << endl;
		return INVALID_SOCKET;
	}

	if ( stricmp( protocol,"tcp" ) == 0 )
		if ( listen(s,5) < 0 )
		{
			cout << "Cannot listen on port: " << service << endl;
			return INVALID_SOCKET;
		}

	return s;
}

/*****************************************************************************
*
*****************************************************************************/
int wait( SOCKET s, int seconds, int msec ) 
{
	int    stat;					
	struct timeval stTimeOut;		
	fd_set stReadFDS;				
	fd_set stXcptFDS;				
	
	stTimeOut.tv_sec  = seconds;	
	stTimeOut.tv_usec = msec;		

	FD_ZERO( &stReadFDS );			
	FD_ZERO( &stXcptFDS );			
	FD_SET( s, &stReadFDS );			
	FD_SET( s, &stXcptFDS );			

	// Check to see if Read access is enabled for socket "s"
	stat = select( -1, &stReadFDS, NULL, &stXcptFDS, &stTimeOut );
	if ( stat == SOCKET_ERROR ) 
	{
		std::cout << std::endl << "wait() function failed" << std::endl;
		stat = 0;
	}
	else if ( stat > 0 ) 
	{
		if ( FD_ISSET( s, &stXcptFDS )) 
			stat = 0;
		
		if ( !FD_ISSET( s, &stReadFDS ))	
			stat = 0;
	}

	return stat;
}

void main (int argc, char *argv[]) {
	char service[MAXBUF] = "echo";		// default service name or port number
	sockaddr_in   csin;					// holds client's socket information
	SOCKET       lsock;					// listening socket handle
	SOCKET       csock;					// connected socket handle
	int clen = sizeof(csin);			// size of client's sockaddr_in struct
	char ip_addr[MAXIPBUF];				// IP address of client
	char buf[MAXBUF];					// buffer to hold incoming text
	int            len;					// number of characters received from client
	int           stat;					// work variable
	bool	debug   = false;
	bool	wait10  = true;

	if (argc > 1) {
		strcpy_s(service, MAXBUF, argv[1]);
	}
	for (int i=2; i<argc; i++) {
		if (_stricmp(argv[i],"debug")==0) {
			debug   = true;
			wait10  = false;
		}
		if (_stricmp(argv[i],"nowait")==0) {
			wait10  = false;
		}
	}

	// Initialize the Windows Sockets TCP/IP library
	WORD wVersionRequired = 0x0101;
	WSADATA wsaData;
	int err = WSAStartup( wVersionRequired, &wsaData );
	if (err) {
		cout << "Unable to initialize Windows Socket library." << endl;
		return;
	}

	// Create a listening socket named lsock
	lsock = passivesock(service,"tcp");
	if (lsock != INVALID_SOCKET) 
	{
		cout << "Waiting for connections..." << endl;
		while (true) 
		{ //Accept connections "forever" 
			csock = accept(lsock,(LPSOCKADDR) &csin, &clen);
			if (csock == INVALID_SOCKET) 
			{
				cout << "Unable to accept connection request" << endl;
			}
			else {
				strcpy_s(ip_addr, MAXIPBUF, inet_ntoa(csin.sin_addr) );
				cout << timestamp()
					 << " - Connection accepted from: " 
					 << ip_addr << endl;

				stat = 1;
				if (wait10) stat = wait(csock,10,0);		// Wait for up to 10 seconds for incoming frame.
				if (stat > 0) {
					len = recv(csock, buf, MAXBUF, 0);
					while (len > 0) 
					{
						if (debug) 
						{
							cout << timestamp() << " - Received data: ";
							for (int i=0; i<len; i++) cout << buf[i];
							cout << endl;
						}
						send(csock, buf, len, 0);
						if (debug) 
						{
							cout << timestamp() << " - Sending  data: ";
							for (int i=0; i<len; i++) cout << buf[i];
							cout << endl;
						}
						if (wait10) stat = wait(csock,10,0);
						if (stat > 0)
							len = recv(csock, buf, MAXBUF, 0);
						else
							len = 0;
					}
				}
				if (debug) 
				{
					cout << timestamp() << " - Shutdown request received!" << endl;
					cout << timestamp() << " - Sending prologue..." << endl;
				}
				strcpy_s(buf, MAXBUF, "Don't go yet!  I've got a little more to data to send to you.\n");
				send(csock,buf,strlen(buf)+1,0);
				strcpy_s(buf, MAXBUF, "This echo server would like to send this string to your client\n  after you have called shutdown().\n");
				send(csock,buf,strlen(buf)+1,0);
				strcpy_s(buf, MAXBUF, "\nNow I'm done. Bye!\n");
				send(csock,buf,strlen(buf)+1,0);
				shutdown(csock,1);
				closesocket(csock);
				cout << timestamp() << " - Connection closed" << endl;

			}
		}
	}
	closesocket(lsock);
	WSACleanup();
}
